package com.bigknow.appstore.proxy.handler.filter;

import com.bigknow.appstore.proxy.cache.ICacheManager;
import com.bigknow.appstore.proxy.enumerate.ErrorType;
import com.bigknow.appstore.proxy.exceptions.ProxyError;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorAssert;
import com.bigknow.appstore.proxy.handler.base.BaseOutboundHandler;
import com.bigknow.appstore.proxy.log.ILogWriter;
import com.bigknow.appstore.proxy.server.IProxyServer;
import com.bigknow.appstore.proxy.util.HandlerVariables;
import com.bigknow.appstore.proxy.vo.ClientErrorCallTimesVo;
import com.bigknow.appstore.proxy.vo.RequestVo;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;

import java.net.InetSocketAddress;
import java.util.concurrent.locks.StampedLock;

/**
 * 如果一个client在一段时间类连续错误访问服务，将由此类进行记录
 * 由
 * Created by chaos on 2016/9/27.
 */
@ChannelHandler.Sharable
public class ProxyErrorFilterHandler extends BaseOutboundHandler {

    public ProxyErrorFilterHandler(IProxyServer proxyServer, ICacheManager cacheManager, ILogWriter writer) {
        super(proxyServer, cacheManager, writer);
    }

    /**
     * 此类需要重新实现此方法，逻辑与其他handler相反，如果有错误代码才处理，否则就不处理
     *
     * @param ctx
     * @param msg
     * @param promise
     * @throws Exception
     */
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        try {

            ErrorAssert.assertFullHttpMsg(ctx, msg, getRequestVo(ctx));
            ErrorAssert.assertFullHttpResponseMsg(ctx, (FullHttpMessage) msg, getRequestVo(ctx));
            String proxyError = ((FullHttpResponse) msg).headers().get("proxyError");
            HttpResponseStatus httpCode = ((FullHttpResponse) msg).status();
            //如果消息头带有proxyError，说明是系统发出的错误信息，不处理，直接过
            if (proxyError != null || httpCode.code() >= 400) {
                write(ctx, (FullHttpMessage) msg, promise);
            } else {
                ctx.write(msg, promise);
            }
        } catch (ProxyError proxyError) {
            //统一的错误处理
            proxyServer.getErrorProcessor().processError(ctx, proxyError);
        }
    }

    @Override
    public void write(ChannelHandlerContext ctx, FullHttpMessage msg, ChannelPromise promise) {
        String key = ((InetSocketAddress) ctx.channel()
                .remoteAddress()).getAddress().getHostAddress();
        if (HandlerVariables.clientErrorCallTimesMap.containsKey(key)) {
            ClientErrorCallTimesVo errorCallTimesVo = HandlerVariables.clientErrorCallTimesMap.get(key);
            errorCallTimesVo.incrementError();
        } else {
            ClientErrorCallTimesVo errorCallTimesVo = new ClientErrorCallTimesVo();
            errorCallTimesVo.incrementError();
            HandlerVariables.clientErrorCallTimesMap.put(key, errorCallTimesVo);
        }
        RequestVo requestVo = getRequestVo(ctx) == null ? HandlerVariables.lastRequestVos.get(key):getRequestVo(ctx);
        if (requestVo == null) {
            writer.writeLog(writer.createErrorLogObject(key, ErrorType.CALL_ERROR
                    , "error url or connection"));
        } else {
            writer.writeLog(writer.createErrorLogObject(requestVo, ErrorType.CALL_ERROR
                    , "error url or connection"));
        }
        ctx.write(msg, promise);
    }
}
